package cn.aicnn.chatssespringboot.util;

import cn.aicnn.chatssespringboot.model.FileMetrics;
import cn.aicnn.chatssespringboot.model.ProjectMetrics;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.stmt.ForStmt;
import com.github.javaparser.ast.stmt.IfStmt;
import com.github.javaparser.ast.stmt.SwitchEntry;
import com.github.javaparser.ast.stmt.WhileStmt;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class JavaProjectMetricsAnalyzer {

    public static ProjectMetrics analyzeProject(String projectRoot) throws Exception {
        ProjectMetrics projectMetrics = new ProjectMetrics();
        Files.walk(Paths.get(projectRoot))
                .filter(p -> p.toString().endsWith(".java"))
                .forEach(p -> {
                    try {
                        FileMetrics fm = analyzeJavaFile(p.toFile());
                        projectMetrics.addFileMetrics(fm);
                    } catch (Exception e) {
                        System.err.println("分析出错: " + p + " - " + e.getMessage());
                    }
                });
        return projectMetrics;
    }

    public static FileMetrics analyzeJavaFile(File javaFile) throws Exception {
        FileMetrics metrics = new FileMetrics();
        metrics.fileName = javaFile.getName();
        List<String> lines = Files.readAllLines(javaFile.toPath());
        metrics.linesOfCode = lines.size();

        // 配置符号解析器（用于识别方法重写）
        CombinedTypeSolver typeSolver = new CombinedTypeSolver();
        typeSolver.add(new ReflectionTypeSolver());
        JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);

        JavaParser parser = new JavaParser();
        parser.getParserConfiguration().setSymbolResolver(symbolSolver);

        FileInputStream in = new FileInputStream(javaFile);
        ParseResult<CompilationUnit> result = parser.parse(in);

        if (result.isSuccessful() && result.getResult().isPresent()) {
            CompilationUnit cu = result.getResult().get();
            List<Comment> comments = cu.getAllContainedComments();
            metrics.commentLines = comments.size();
            metrics.commentDensity = metrics.linesOfCode == 0 ? 0 :
                    (double) metrics.commentLines / metrics.linesOfCode * 100;

            Set<String> calledMethods = new HashSet<>();
            Set<String> accessedTypes = new HashSet<>();
            Map<String, List<String>> classMethodsMap = new HashMap<>();

            new VoidVisitorAdapter<Void>() {
                private int currentClassMethodCount = 0;
                private int currentClassOverrideCount = 0;
                private int currentClassAdditionCount = 0;

                @Override
                public void visit(ClassOrInterfaceDeclaration n, Void arg) {
                    // 保存父类的统计值（用于嵌套类情况）
                    int parentMethodCount = currentClassMethodCount;
                    int parentOverrideCount = currentClassOverrideCount;
                    int parentAdditionCount = currentClassAdditionCount;

                    // 重置当前类的统计
                    currentClassMethodCount = 0;
                    currentClassOverrideCount = 0;
                    currentClassAdditionCount = 0;

                    // ============= 新增：ILF/EIF统计 =============
                    if (n.isAnnotationPresent("Data")) {
                        metrics.internalLogicalFiles++;
                    }
                    if (n.isAnnotationPresent("FeignClient")) {
                        // 统计该FeignClient接口中的所有Mapping方法数量
                        long mappingCount = n.getMethods().stream()
                                .filter(m -> m.getAnnotations().stream()
                                        .anyMatch(a -> a.getNameAsString().endsWith("Mapping")))
                                .count();

                        // 直接累加Mapping方法的数量（而不是+1）
                        metrics.externalInterfaces += mappingCount;
                    }

                    super.visit(n, arg);
                    List<String> methodNames = new ArrayList<>();

                    // 检查是否有父类（通过extends判断）
                    boolean hasParentClass = !n.getExtendedTypes().isEmpty();

                    for (MethodDeclaration m : n.getMethods()) {
                        methodNames.add(m.getNameAsString());
                        metrics.wmc++;
                        currentClassMethodCount++;

                        // 方法重写检测（备选方案）
                        boolean isOverride = m.getAnnotationByName("Override").isPresent();

                        if (isOverride) {
                            // 有@Override注解的方法
                            currentClassOverrideCount++;
                        } else if (hasParentClass) {
                            // 有父类但没有@Override注解的方法，视为新增
                            currentClassAdditionCount++;
                        } else {
                            // 没有父类的类，所有方法都是新增
                            currentClassAdditionCount++;
                        }

                        metrics.cyclomaticComplexity += calculateCyclomaticComplexity(m);
                    }

                    classMethodsMap.put(n.getNameAsString(), methodNames);

                    // 设置LK度量值
                    metrics.cs = currentClassMethodCount;          // 类规模
                    metrics.noo = currentClassOverrideCount;      // 方法重写数
                    metrics.noa = currentClassAdditionCount;      // 新增方法数

                    // 计算特征化指数 (避免除以0)
                    metrics.si = (metrics.noo + metrics.noa) > 0 ?
                            (double) metrics.noo / (metrics.noo + metrics.noa) : 0.0;

                    // 恢复父类的统计值（用于嵌套类）
                    currentClassMethodCount = parentMethodCount;
                    currentClassOverrideCount = parentOverrideCount;
                    currentClassAdditionCount = parentAdditionCount;
                }

                @Override
                public void visit(MethodDeclaration n, Void arg) {
                    // ============= 新增：EI/EO/EQ统计 =============
                    if (n.getAnnotationByName("PostMapping").isPresent()) {
                        metrics.externalInputs++;
                    }
                    else if (n.getAnnotationByName("GetMapping").isPresent()) {
                        String returnType = n.getType().toString().toLowerCase();
                        String methodName = n.getNameAsString().toLowerCase();

                        if (methodName.contains("report") ||
                                methodName.contains("export") ||
                                returnType.contains("vo")) {
                            metrics.externalOutputs++;
                        } else {
                            metrics.externalQueries++;
                        }
                    }
                    super.visit(n, arg);
                }

                @Override
                public void visit(MethodCallExpr n, Void arg) {
                    super.visit(n, arg);
                    calledMethods.add(n.getNameAsString());
                    n.getScope().ifPresent(s -> accessedTypes.add(s.toString()));
                }

                @Override
                public void visit(FieldDeclaration n, Void arg) {
                    super.visit(n, arg);
                    accessedTypes.add(n.getElementType().asString());
                }

                @Override
                public void visit(ObjectCreationExpr n, Void arg) {
                    super.visit(n, arg);
                    accessedTypes.add(n.getType().getNameAsString());
                }
            }.visit(cu, null);

            metrics.rfc = metrics.wmc + calledMethods.size();
            metrics.cbo = accessedTypes.size();

            metrics.useCases = metrics.externalInputs + metrics.externalOutputs + metrics.externalQueries;
            metrics.useCasePoints = metrics.useCases + metrics.actors;
        }

        return metrics;
    }

    private static int calculateCyclomaticComplexity(MethodDeclaration method) {
        int complexity = 1;
        complexity += method.findAll(IfStmt.class).size();
        complexity += method.findAll(ForStmt.class).size();
        complexity += method.findAll(WhileStmt.class).size();
        complexity += method.findAll(SwitchEntry.class).size();
        return complexity;
    }
}